import { defineStore } from "pinia";
import * as apis from "@/api/user";
import { getToken, setToken, removeToken } from "@/utils/token-utils";
import type { IUserInfo } from "@/api/user";
import { requestLogOut } from "@/api/user";
import { staticRoutes, allAsyncRoutes, anyRoute } from "@/router/routes";
import type { RouteRecordRaw } from "vue-router";
import router from "@/router/index";
import { cloneDeep } from "lodash";
interface IState {
  token: string;
  userInfo: IUserInfo;
  menuRoutes: RouteRecordRaw[];
}

/**
 * 根据用户权限得到能够访问的路由信息
 * routes: 需要做筛选的原始路由数组
 * userRoutes: 当前用户的路由权限信息
 * 返回当前用户能够访问的路由数组
 */
function getAsyncRoutes(
  routes: RouteRecordRaw[],
  userRoutes: string[]
): RouteRecordRaw[] {
  // 存储满足条件的route
  const menuRoutes: RouteRecordRaw[] = [];

  routes.forEach((r) => {
    // 如果当前 r 这个路由对象的 name 在 userRoutes 中存在，那么r就满足了我们的条件，我们需要把这个 r 添加到 menuRoutes
    if (userRoutes.includes(r.name! as string)) {
      // 即使 r 满足了条件，但是我们也不能直接把 r 添加到 menuRoutes 中，因为它如果有 children 的话，children 中路由我们还需要去做判断

      // 我们还得处理一下这个 r 的 children
      if (Array.isArray(r.children)) {
        // getAsyncRoutes(r.children, userRoutes) 得到满足条件的 children
        // 并且还需要用得到的结果去覆盖 r.children
        r.children = getAsyncRoutes(r.children, userRoutes);
      }
      // 处理完 children 以后，再把 r 的值添加到 menuRoutes
      menuRoutes.push(r);
    }
  });

  return menuRoutes;
}

function resetRoute() {
  allAsyncRoutes.forEach((route) => {
    // 从 router 中移除所有的 route
    // 先判断一下当前 route 是否在 router 路由表中存在
    if (router.hasRoute(route.name!)) {
      router.removeRoute(route.name!);
    }
  });
  router.removeRoute(anyRoute.name!);
}

/**
 * 我们可以通过 defineStore 的 第一个泛型参数来设置 id 的类型（一般它是字符串）
 * 通过第二个参数来设置state返回的数据类型
 */
export const useUserStore = defineStore<string, IState, any, any>("user", {
  state() {
    return {
      // 用户 token,从localstorage获取token
      token: getToken() || "",
      // 用户基本信息
      userInfo: {
        // 用户名
        name: "",
        // 头像
        avatar: "",
        // 路由访问权限
        routes: [],
        // 页面功能按钮权限
        buttons: [],
        // 角色信息
        roles: [],
      },
      // 存放后台菜单路由对象的数组，我们的后台管理页面左侧的菜单是通过这个数组来进行渲染的
      menuRoutes: [],
    };
  },

  actions: {
    // 如果函数调用的时候加了async  说明此函数调用的时候一定会返回一个promise对象
    //即使我们没有写return  那么也会返回一个成功状态 且值为undefined的promise
    async login(name: string, password: string) {
      try {
        const res = await apis.login(name, password);
        this.token = res.token;
        setToken(this.token);
      } catch (e) {
        // 如果这里我们没有抛出错误
        return Promise.reject(e);
      }
    },

    async getUserInfo() {
      try {
        const res = await apis.requestUserInfo();

        // const { name, avatar, routes, buttons, roles } = res;
        // this.userInfo.name = name;
        // this.userInfo.avatar = avatar;
        // this.userInfo.routes = routes;
        // this.userInfo.buttons = buttons;
        // this.userInfo.roles = roles;
        this.userInfo = { ...res };

        // 获取到用户的身份权限信息以后，我们再去根据当前用户的身份权限信息从 allAsyncRoutes 数组中去检索菜单路由信息
        // 因为我们把不需要检索的菜单信息分开存放了，所以这一组路由我们直接放在 menuRoutes 中
        this.menuRoutes = [...staticRoutes];
        // 检索 allAsyncRoutes 中有哪一些路由是需要生成菜单数据的

        // 通过 userInfo 下的 routes 就能得到当前用户的菜单权限数组信息
        // console.log("userInfo.routes", this.userInfo.routes);

        // 遍历 allAsyncRoutes，获取到每一个路由对象的 name，看一下当前路由对象的 name 是否在 this.userInfo.routes 这个数组中，如果在，则表示当前路由是需要生成菜单的
        // allAsyncRoutes.forEach((route) => {
        //   // console.log(route.name);
        //   if (this.userInfo.routes.includes(route.name)) {
        //     // 我们不能只考虑一层路由是否能够访问，还应该考虑子级路由，就需要递归
        //     this.menuRoutes.push(route);
        //   }
        // });

        // 调用递归函数得到当前用户能够访问的路由信息
        // 问题：allAsyncRoutes 不能被修改，否则会影响下次登录的过滤
        //    解决：把 allAsyncRoutes 进行深度克隆，传入克隆后的对象，这样就不会影响原始的 allAsyncRoutes
        const userRoutes = getAsyncRoutes(
          cloneDeep(allAsyncRoutes),
          this.userInfo.routes
        );
        // console.log(`allAsyncRoutes`, allAsyncRoutes);

        // console.log(userRoutes);

        this.menuRoutes.push(...userRoutes);

        // console.log(`router`, router);
        // 这个对象是整个应用的路由器对象，提供了很多的方法
        // https://router.vuejs.org/api/interfaces/Router.html
        // 获取当前 router 下所有的被注册过的路由对象（路由表）
        // console.log(router.getRoutes());
        // console.log(`getRoutes`, router.getRoutes());
        // 注册 any路由
        router.addRoute(anyRoute);

        // 所有我们需要把当前匹配路由信息添加到菜单的同时，还需要把当前路由注册到路由表中：
        // addRoute() 接收的路由对象，所以我们需要先对 userRoutes 进行循环，添加
        userRoutes.forEach((r) => {
          // addRoute 内部会递归进行注册，所以，当前r的子路由不需要单独进行注册
          router.addRoute(r);
        });
        // console.log(router.getRoutes());
      } catch (e) {
        // 在这里清除用户信息
        this.clearUserInfo();
        return Promise.reject(e);
      }
    },

    async reset() {
      try {
        //调用后端接口退出
        await requestLogOut();
        //前端也要清除和用户相关的数据
        this.clearUserInfo();
        // 重置路由表信息
        resetRoute();
        // console.log(router.getRoutes());
      } catch (e) {}
    },
    clearUserInfo() {
      //清空仓库的数据
      this.token = "";
      this.userInfo = {
        name: "",
        avatar: "",
        routes: [],
        buttons: [],
        roles: [],
      };
      //localstorage里面的token也要删除
      removeToken();
    },
  },
});
